package com.xiaoxin.experience.service.hls.common;

import com.xiaoxin.experience.service.hls.properties.HlsProperties;
import com.xiaoxin.experience.util.DownloadUtil;
import com.xiaoxin.experience.util.FileUtil;
import com.xiaoxin.experience.util.ThreadUtil;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author xiaoxin
 */
public class TsDownloader
{
    private TsDownloader() {}

    private final static TsDownloader instance = new TsDownloader();

    private HlsProperties hlsProperties;

    public static TsDownloader getInstance()
    {
        return instance;
    }
    private volatile ThreadPoolExecutor tsDownloadThreadPool = null;

    public void setHlsProperties(HlsProperties hlsProperties)
    {
        this.hlsProperties = hlsProperties;
    }

    private static final Object CREATE_TS_THREAD_POOL_LOCK = new Object();
    public String defaultDownload(HlsParser hlsParser,HlsTask hlsTask)
    {
        List<CompletableFuture<File>> futures = new ArrayList<>();
        List<String> tsDownloadPaths = hlsParser.getTsDownloadPaths();
        AtomicInteger tsFileNameCreator = new AtomicInteger(0);
        String tsSaveDir = FileUtil.dirPathComplete(hlsTask.getSavePath()) + hlsTask.getName();
        FileUtil.createDirs(tsSaveDir);
        for (String tsDownloadPath : tsDownloadPaths)
        {
            CompletableFuture<File> fileCompletableFuture = CompletableFuture.supplyAsync(()->
            {
                if (hlsTask.isRemoved())
                {
                    return null;
                }
                int serial = tsFileNameCreator.getAndIncrement();
                String tsFileName = serial + ".ts";
                String tsFileSavePath = FileUtil.dirPathComplete(tsSaveDir) + tsFileName;
                byte[] bytes = getBytes(tsDownloadPath);
                recordFailFileCount(hlsTask,bytes);
                FileUtil.writeToFile(hlsParser.decrypt(bytes),tsFileSavePath,false);
                hlsTask.getFinishFileCount().getAndIncrement();
                return new File(tsFileSavePath);
            },tsDownloadThreadPool).exceptionally(e ->
            {
                hlsTask.getFinishFileCount().getAndIncrement();
                return null;
            });
            futures.add(fileCompletableFuture);
        }
        // 保存索引文件
        FileUtil.writeToFile(hlsParser.getHlsIndex(),FileUtil.dirPathComplete(tsSaveDir) + hlsTask.getName() + ".m3u8",false);
        hlsTask.setTotalFileCount(futures.size());
        String dstFilePath = FileUtil.dirPathComplete(hlsTask.getSavePath()) + hlsTask.getName() + ".mp4";
        long mp4FileByteSize = FileUtil.asyncMerge(dstFilePath, futures);
        hlsTask.setFileSize(FileUtil.getHumanFileSize(mp4FileByteSize));
        hlsTask.setStatus(TaskStatus.FINISH);
//        FileUtil.deleteFile(tsSaveDir);
        return dstFilePath;
    }

    public String generalDownload(HlsParser hlsParser,HlsTask hlsTask)
    {
        String dstFilePath = FileUtil.dirPathComplete(hlsTask.getSavePath()) + hlsTask.getName() + ".mp4";
        List<String> tsDownloadPaths = hlsParser.getTsDownloadPaths();
        List<CompletableFuture<byte[]>> futures = new ArrayList<>();
        for (String tsDownloadPath : tsDownloadPaths)
        {
            CompletableFuture<byte[]> fileCompletableFuture = CompletableFuture.supplyAsync(()->
            {
                if (hlsTask.isRemoved())
                {
                    return null;
                }
                byte[] bytes = getBytes(tsDownloadPath);
                hlsTask.getFinishFileCount().getAndIncrement();
                recordFailFileCount(hlsTask,bytes);
                return hlsParser.decrypt(bytes);
            },tsDownloadThreadPool).exceptionally(e ->
            {
                hlsTask.getFinishFileCount().getAndIncrement();
                return null;
            });
            futures.add(fileCompletableFuture);
        }
        hlsTask.setTotalFileCount(futures.size());
        FileUtil.createDirs(hlsTask.getSavePath());
        long mp4FileByteSize = FileUtil.writeToFile(futures, dstFilePath);
        if (mp4FileByteSize == 0)
        {
            hlsTask.setStatus(TaskStatus.ERROR);
            return null;
        }
        hlsTask.setFileSize(FileUtil.getHumanFileSize(mp4FileByteSize));
        hlsTask.setStatus(TaskStatus.FINISH);
        return dstFilePath;
    }

    private byte[] getBytes(String downloadPath)
    {
        if (hlsProperties.isUnlessRetry())
        {
            return DownloadUtil.downloadUntilSuccess(downloadPath);
        }
        else
        {
            return DownloadUtil.downloadLimitRetry(downloadPath, hlsProperties.getRetryTimes());
        }
    }

    private void recordFailFileCount(HlsTask hlsTask,byte[] bytes)
    {
        if (Objects.isNull(bytes) || bytes.length == 0)
        {
            hlsTask.getFailFileCount().getAndIncrement();
        }
    }


    public void initTsDownloadThreadPool(int size)
    {
        if (Objects.isNull(tsDownloadThreadPool))
        {
            synchronized (CREATE_TS_THREAD_POOL_LOCK)
            {
                if (Objects.isNull(tsDownloadThreadPool))
                {
                    tsDownloadThreadPool = ThreadUtil.getFixThreadPoolExecutor(size,"ts-downloader");
                }
            }
        }
    }

    public enum DownloadMethod
    {
        DEFAULT(0),
        GENERAL(1);

        DownloadMethod(int type)
        {
            this.type = type;
        }

        public final int type;

        public static DownloadMethod of(int type)
        {
            for (DownloadMethod value : values())
            {
                if (value.type == type)
                {
                    return value;
                }
            }
            return DEFAULT;
        }
    }
}
